﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;

namespace Genuilder.Extensibility
{

	//public class ManyToOneCodeDependency : CodeDependencyBase
	//{
	//    public ManyToOneCodeDependency(string target)
	//    {
	//        _Sources.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(_Sources_CollectionChanged);
	//    }

	//    void _Sources_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
	//    {
	//        NeedUpdate = true;
	//    }
	//    private readonly ObservableCollection<String> _Sources = new ObservableCollection<string>();
	//    public IList<String> Sources
	//    {
	//        get
	//        {
	//            return _Sources;
	//        }
	//    }
	//    public event Action<ManyToOneCodeDependency> NeedUpdated;
	//    protected internal override void OnNeedUpdate()
	//    {
	//        NeedUpdate = false;
	//        if(NeedUpdated != null)
	//            NeedUpdated(this);
	//    }
	//}

	public abstract class CodeDependencyBase
	{
		internal bool NeedUpdate
		{
			get;
			set;
		}

		internal abstract void OnNeedUpdate(IEnumerable<CodeItem> items);
	}

	public interface DependencyNodeRepository
	{
		DependencyNode Get(CodeItem item);
	}

	public class DependencyNode
	{
		readonly DependencyNodeRepository _DependencyNodeRepository;
		readonly TaskQueue _Tasks;

		public DependencyNode(CodeItem item, DependencyNodeRepository dependencyNodeRepository, TaskQueue tasks, bool isRootSource)
		{
			if(item == null)
				throw new ArgumentNullException("item");
			if(dependencyNodeRepository == null)
				throw new ArgumentNullException("codeItemsRepository");
			_Tasks = tasks;
			_IsRootSource = isRootSource;
			_Item = item;
			_DependencyNodeRepository = dependencyNodeRepository;
		}


		List<DependencyNode> _Sources = new List<DependencyNode>();
		List<DependencyNode> _Targets = new List<DependencyNode>();

		public IEnumerable<DependencyNode> Sources
		{
			get
			{
				return _Sources;
			}
		}

		public IEnumerable<DependencyNode> Targets
		{
			get
			{
				return _Targets;
			}
		}
		private readonly bool _IsRootSource;
		public bool IsRootSource
		{
			get
			{
				return _IsRootSource;
			}
		}
		private readonly CodeItem _Item;
		public CodeItem Item
		{
			get
			{
				return _Item;
			}
		}


		public CodeDependency SourceOf(CodeItem item)
		{
			CodeDependency dependency = new CodeDependency(Item, item, _Tasks);
			var target = _DependencyNodeRepository.Get(item);
			_Targets.Add(target);
			target._Sources.Add(this);
			return dependency;
		}

		public CodeDependency TargetOf(CodeItem item)
		{
			if(_IsRootSource)
				throw new InvalidOperationException("A root source file cannot be a target of another source file");
			var node = _DependencyNodeRepository.Get(item);
			return node.SourceOf(item);
		}
	}

	public delegate void CodeDependencyHandler(CodeDependency sender, CodeItem target);
	public class CodeDependency
	{
		TaskQueue _Tasks;
		internal CodeDependency(CodeItem source, CodeItem target, TaskQueue tasks)
		{
			if(source == null)
				throw new ArgumentNullException("source");
			if(target == null)
				throw new ArgumentNullException("target");
			_Source = source;
			_Target = target;
			_Tasks = tasks;
			_Source.StateChanged += new StateChangedHandler(_Source_StateChanged);
			_Source.ContentChanged += new ContentChangedHandler(_Source_ContentChanged);
			_Target.StateChanged += new StateChangedHandler(_Target_StateChanged);
			_Target.ContentChanged += new ContentChangedHandler(_Target_ContentChanged);
		}

		bool _UpdatingTarget;

		void _Target_ContentChanged(CodeItem sender)
		{
			OnShouldUpdate();
		}



		void _Target_StateChanged(CodeItem sender, CodeItemState newState, CodeItemState oldState)
		{
			if(newState == CodeItemState.Deleted || newState == CodeItemState.Excluded)
				OnShouldUpdate();
		}


		public event CodeDependencyHandler ShouldUpdateTarget;

		void _Source_ContentChanged(CodeItem sender)
		{
			OnShouldUpdate();
		}

		private void OnShouldUpdate()
		{

			if(!_UpdatingTarget)
				_Tasks.Enqueue(this, "Update", () =>
				{
					if(!_UpdatingTarget)
					{
						_UpdatingTarget = true;

						if(Target.State == CodeItemState.Exist)
						{
							if(ShouldUpdateTarget != null)
								ShouldUpdateTarget(this, Target);
						}
						_Tasks.Enqueue(() =>
						{
							_UpdatingTarget = false;
						});
					}
				});

		}

		void _Source_StateChanged(CodeItem sender, CodeItemState newState, CodeItemState oldState)
		{
			if(newState == CodeItemState.Deleted)
				Target.Delete();
			if(newState == CodeItemState.Excluded)
				Target.Exclude();
		}

		private readonly CodeItem _Source;
		public CodeItem Source
		{
			get
			{
				return _Source;
			}
		}

		private readonly CodeItem _Target;
		public CodeItem Target
		{
			get
			{
				return _Target;
			}
		}
	}
}
